﻿using MQTTClient.Bll;
using MQTTClient.Model;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MQTTClient
{
    public partial class Form1 : Form
    {
        private delegate void AddItemToListBoxDelegate(string str);
        private delegate void AddItemToListBoxDelegate2(string str);
        /// <summary>
        /// 客户端列表显示
        /// </summary>
        /// <param name="str"></param>
        private delegate void AddItemToListBoxDelegate3(string str);
        /// <summary>
        /// 所有聊天消息显示
        /// </summary>
        /// <param name="str"></param>
        private delegate void messageBoxDelegate(string str);
        private delegate void ClearToListBoxDelegate3();
        MQTTClientBll mqttClientBll = new MQTTClientBll();
        PayloadBll payloadBll = new PayloadBll();
        /// <summary>
        /// 此客户端登陆后是否加载了所有客户端列表  只允许上线加载一次。
        /// </summary>
        bool isLoadAllClient = false;

        public Form1()
        {
            InitializeComponent();
        }
        private void CreateMQTTClient()
        {
            //实例化 创建客户端对象
            var Factory = new MqttFactory();
            mqttClientBll.MQTTClient = Factory.CreateMqttClient();
            mqttClientBll.MQTTClient.ApplicationMessageReceived += MqttClient_ApplicationMessageReceived;//客户端收到消息
            mqttClientBll.MQTTClient.Connected += MqttClient_Connected;//处理客户端与服务端连接
            mqttClientBll.MQTTClient.Disconnected += MqttClient_Disconnected;//处理客户端从服务端断开  

        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            if (mqttClientBll.MQTTClient == null)
            {
                CreateMQTTClient();
                //调用异步方法连接到服务端
                Task<MqttClientConnectResult> state = mqttClientBll.MQTTClient.ConnectAsync(ClientConnectOption());
                if (state.IsFaulted)
                {
                    mqttClientBll.MQTTClient = null;
                    MessageBox.Show("MQTT客户端已断开！");
                }
                else
                {
                   // MessageBox.Show("MQTT客户端已连接！");
                }
            }
            else
            {
                MessageBox.Show("MQTT客户端已连接！");
            }
        }
        /// <summary>
        /// 创建客户端连接参数对象
        /// </summary>
        /// <returns></returns>
        public IMqttClientOptions ClientConnectOption()
        {
            mqttClientBll.ClientId = textBox5.Text.Trim();
            mqttClientBll.IP = textBox1.Text.Trim();
            mqttClientBll.Port = int.Parse(textBox2.Text.Trim());
            mqttClientBll.UserName = textBox3.Text.Trim();
            mqttClientBll.pwd = textBox4.Text.Trim();

            //连接到服务器前，获取所需要的MqttClientTcpOptions 对象的信息
            var options = new MqttClientOptionsBuilder()
            .WithClientId(mqttClientBll.ClientId)                    // clientid是设备id
            .WithTcpServer(mqttClientBll.IP, mqttClientBll.Port)              //onenet ip：183.230.40.39    port:6002
            .WithCredentials(mqttClientBll.UserName, mqttClientBll.pwd)      //username为产品id       密码为鉴权信息或者APIkey
                                                                         //.WithTls()//服务器端没有启用加密协议，这里用tls的会提示协议异常
            .WithCleanSession(false)
            .WithKeepAlivePeriod(TimeSpan.FromSeconds(2000))
            .Build();
            return options;
        }

        public void MqttClient_Connected(object sender, MqttClientConnectedEventArgs e)
        {
            AddItemToListBox("客户端连接服务器事件:已连接到服务器！");
            //订阅公共主题 便于客户端之间以后通讯 根据内部数据体内容，处理后续的业务逻辑
            mqttClientBll.Subscribe_PublicTopic();
            //订阅自己名称的主题，用于其他人和自己通讯。
            mqttClientBll.Subscribe_Topic(mqttClientBll.ClientId);


            //发送上线通知 创建客户端对象
            ClientPayloadDataModel cdata = new ClientPayloadDataModel();
            cdata.ClientName = mqttClientBll.ClientId;
            cdata.OnLineTime = DateTime.Now.ToString();
            //创建统一的消息体，准备发送数据
            PayloadDataBase pd = new PayloadDataBase();
            pd.PayloadType = 2;
            pd.DataContent = payloadBll.GetPayloadDataJSONStr_ByOBJ(cdata);
            string jsonstr = payloadBll.GetPayloadDataJSONStr_ByOBJ(pd);
            //发布消息
            mqttClientBll.Publish_PublicData(jsonstr);
        }

        public void MqttClient_Disconnected(object sender, MqttClientDisconnectedEventArgs e)
        {
            mqttClientBll.MQTTClient = null;
            AddItemToListBox("客户端断开服务器事件:已断开服务器！" + e.Exception.Message);
        }

        private void MqttClient_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
        {
            AddItemToListBox("接收消息事件:" + e.ApplicationMessage.Topic + ",数据内容" + Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
            AddItemToListBox2(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));

            PayloadDataBase pd = new PayloadDataBase();
            string str = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
            pd = payloadBll.GetOBJ_ByPayloadDataJSONStr(pd, str);//获得统一的数据体对象
            //公共主题消息
            if (e.ApplicationMessage.Topic.Equals(SubscribeModel.PublicTopic))
            {
                if (pd.PayloadType == 1 || pd.PayloadType == 2 || pd.PayloadType == 3)
                {
                    PublicPayloadData(pd);
                }
            }
            else if (e.ApplicationMessage.Topic.Equals(mqttClientBll.ClientId))//接收自己的主题消息
            {
                MyPayloadData(pd);
            }
            else if (pd.PayloadType == 0)//其它所有私有消息
            {
                MyPayloadData(pd);
            }
        }
        /// <summary>
        /// 处理自己私有的主题数据
        /// </summary>
        private void MyPayloadData(PayloadDataBase pd)
        {
            ClientPayloadDataModel cdata = new ClientPayloadDataModel();
            cdata = payloadBll.GetOBJ_ByPayloadDataJSONStr(cdata, pd.DataContent);
            //私聊时 只有自己或发送方的数据才显示
            if (mqttClientBll.ClientId.Equals(cdata.ClientName) || mqttClientBll.ClientId.Equals(cdata.SendDataClientName))
            {
                string tmp = cdata.IsSiLiao ? "[私聊]" : "";
                AddmessageBox(tmp + cdata.SendDataClientName + "对" + cdata.ClientName + "说:" + cdata.DataMessage + "--" + cdata.DataMessageTime);
            }
        }
        /// <summary>
        /// 处理公共主题数据
        /// </summary>
        private void PublicPayloadData(PayloadDataBase pd)
        {
            //保证两个并行的条件判断，为2时 单独加载一个客户名称。 如果为1且没加载过，清除所有重新加载客户列表
            if (pd.PayloadType == 2)//单个客户端上线
            {
                ClientPayloadDataModel cdata = new ClientPayloadDataModel();
                cdata = payloadBll.GetOBJ_ByPayloadDataJSONStr(cdata, pd.DataContent);
                //订阅此客户名称的主题，避免后面和他私聊时，自己收不到自己发送给他的消息。
                mqttClientBll.Subscribe_Topic(cdata.ClientName);
                if (listBox1.Items.IndexOf(cdata.ClientName + "--" + cdata.OnLineTime) == -1)
                    AddItemToListBox3(cdata.ClientName + "--" + cdata.OnLineTime);
            }
            else if (pd.PayloadType == 1)//获得所有客户端名称
            {
                if (!isLoadAllClient)
                {
                    isLoadAllClient = true;
                    ClearToListBox3();//清除所有客户 重新加载客户列表
                    ClientListModel clistdata = new ClientListModel();
                    clistdata = payloadBll.GetOBJ_ByPayloadDataJSONStr(clistdata, pd.DataContent);
                    foreach (ClientPayloadDataModel c in clistdata.ClientList)
                    {
                        //订阅此客户名称的主题，避免后面和他私聊时，自己收不到自己发送给他的消息。
                        mqttClientBll.Subscribe_Topic(c.ClientName);
                        if (listBox1.Items.IndexOf(c.ClientName + "--" + c.OnLineTime) == -1)
                            AddItemToListBox3(c.ClientName + "--" + c.OnLineTime);
                    }
                }
            }
            else if (pd.PayloadType == 3)//获得公共聊天数据
            {
                ClientPayloadDataModel cdata = new ClientPayloadDataModel();
                cdata = payloadBll.GetOBJ_ByPayloadDataJSONStr(cdata, pd.DataContent);
                AddmessageBox("[公聊]" + cdata.SendDataClientName + "对" + cdata.ClientName + "说:" + cdata.DataMessage + "--" + cdata.DataMessageTime);
            }
        }
        /// <summary>
        /// 发布数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            string topic = textBox7.Text.Trim();
            string data = textBox8.Text.Trim();
            ClientPayloadDataModel cdata = new ClientPayloadDataModel();
            cdata.IsSiLiao = checkBox1.Checked;
            cdata.SendDataClientName = mqttClientBll.ClientId;
            cdata.ClientName = topic;
            cdata.DataMessage = data;
            cdata.DataMessageTime = DateTime.Now.ToString();
            //创建统一的消息体，准备发送数据
            PayloadDataBase pd = new PayloadDataBase();
            pd.DataContent = payloadBll.GetPayloadDataJSONStr_ByOBJ(cdata);

            if (!checkBox1.Checked)//公聊
            {
                pd.PayloadType = 3;
                string jsonstr = payloadBll.GetPayloadDataJSONStr_ByOBJ(pd);
                mqttClientBll.Publish_PublicData(jsonstr);
            }
            else
            {
                pd.PayloadType = 0;
                string jsonstr = payloadBll.GetPayloadDataJSONStr_ByOBJ(pd);
                mqttClientBll.Publish_Data(topic, jsonstr);
            }

        }
        /// <summary>
        /// 订阅数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            string topic = textBox6.Text.Trim();
            mqttClientBll.Subscribe_Topic(topic);

        }

        private void AddItemToListBox(string str)
        {
            if (richTextBox1.InvokeRequired)
            {
                AddItemToListBoxDelegate d = AddItemToListBox;
                richTextBox1.Invoke(d, str);
            }
            else
            {
                string str2 = richTextBox1.Text.Insert(0, str + "  " + DateTime.Now.ToString() + "\r\n");
                richTextBox1.Text = str2;
            }
        }
        private void AddItemToListBox2(string str)
        {
            if (richTextBox2.InvokeRequired)
            {
                AddItemToListBoxDelegate2 d = AddItemToListBox2;
                richTextBox2.Invoke(d, str);
            }
            else
            {
                richTextBox2.Text = richTextBox2.Text.Insert(0, str + "  " + DateTime.Now.ToString() + "\r\n");
            }
        }
        private void AddmessageBox(string str)
        {
            if (messageBox.InvokeRequired)
            {
                messageBoxDelegate d = AddmessageBox;
                messageBox.Invoke(d, str);
            }
            else
            {
                messageBox.AppendText(str + "\r\n");
                messageBox.ScrollToCaret();
            }
        }

        /// <summary>
        /// 客户列表显示
        /// </summary>
        /// <param name="str"></param>
        private void AddItemToListBox3(string str)
        {
            if (listBox1.InvokeRequired)
            {
                AddItemToListBoxDelegate3 d = AddItemToListBox3;
                listBox1.Invoke(d, str);
            }
            else
            {

                listBox1.Items.Insert(0, str);
            }
        }
        /// <summary>
        /// 清除客户端列表数据
        /// </summary>
        private void ClearToListBox3()
        {
            if (listBox1.InvokeRequired)
            {
                ClearToListBoxDelegate3 d = ClearToListBox3;
                listBox1.Invoke(d);
            }
            else
            {
                listBox1.Items.Clear();
            }
        }

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listBox1.SelectedItem != null)
            {

                int len = listBox1.SelectedItem.ToString().IndexOf("--");
                textBox7.Text = listBox1.SelectedItem.ToString().Substring(0, len);
            }

        }
    }
}